//! 告警管理器

use crate::matcher::{ScanResult, Severity};
use crate::ScanError;
use async_trait::async_trait;
use chrono::{DateTime, Timelike, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tracing::{error, info, warn};

/// 告警渠道
#[async_trait]
pub trait AlertChannel: Send + Sync {
    /// 发送告警
    async fn send_alert(&self, alert: &Alert) -> Result<(), ScanError>;
    /// 渠道名称
    fn name(&self) -> &str;
    /// 检查渠道是否可用
    async fn is_available(&self) -> bool;
}

/// 告警消息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Alert {
    /// 告警ID
    pub id: String,
    /// 告警标题
    pub title: String,
    /// 告警内容
    pub message: String,
    /// 严重程度
    pub severity: Severity,
    /// 扫描结果ID
    pub scan_result_id: String,
    /// 目标URL
    pub target_url: String,
    /// 规则名称
    pub rule_name: String,
    /// 告警时间
    pub timestamp: DateTime<Utc>,
    /// 标签
    pub tags: Vec<String>,
    /// 附加信息
    pub metadata: HashMap<String, String>,
}

impl Alert {
    /// 从扫描结果创建告警
    pub fn from_scan_result(result: &ScanResult) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            title: format!("Security Alert: {}", result.title),
            message: format!(
                "Vulnerability detected: {}\nURL: {}\nSeverity: {:?}\nDescription: {}",
                result.title,
                result.url,
                result.severity,
                result.description
            ),
            severity: result.severity.clone(),
            scan_result_id: result.id.clone(),
            target_url: result.url.clone(),
            rule_name: result.rule_name.clone(),
            timestamp: Utc::now(),
            tags: result.tags.clone(),
            metadata: {
                let mut meta = HashMap::new();
                if let Some(method) = &result.method {
                    meta.insert("method".to_string(), method.clone());
                }
                if let Some(status) = result.status_code {
                    meta.insert("status_code".to_string(), status.to_string());
                }
                meta.insert("confidence".to_string(), result.confidence.to_string());
                meta
            },
        }
    }

    /// 获取严重程度的颜色代码
    pub fn severity_color(&self) -> &str {
        self.severity.color_code()
    }

    /// 获取格式化的消息
    pub fn formatted_message(&self) -> String {
        format!(
            "{}[{:?}] {}{}\n\nURL: {}\nRule: {}\nSeverity: {:?}\nTime: {}\n\n{}",
            self.severity_color(),
            self.severity,
            self.title,
            "\x1b[0m", // 重置颜色
            self.target_url,
            self.rule_name,
            self.severity,
            self.timestamp.format("%Y-%m-%d %H:%M:%S UTC"),
            self.message
        )
    }
}

/// 告警配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertConfig {
    /// 是否启用告警
    pub enabled: bool,
    /// 告警规则
    pub rules: Vec<AlertRule>,
    /// 全局设置
    pub global_settings: AlertGlobalSettings,
}

impl Default for AlertConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            rules: vec![
                AlertRule::new("Critical", Severity::Critical, true),
                AlertRule::new("High", Severity::High, true),
                AlertRule::new("Medium", Severity::Medium, false),
                AlertRule::new("Low", Severity::Low, false),
                AlertRule::new("Info", Severity::Info, false),
            ],
            global_settings: AlertGlobalSettings::default(),
        }
    }
}

/// 告警规则
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertRule {
    /// 规则名称
    pub name: String,
    /// 严重程度
    pub severity: Severity,
    /// 是否启用
    pub enabled: bool,
    /// 告警频率限制 (秒)
    pub rate_limit_seconds: Option<u64>,
    /// 标签过滤
    pub tag_filters: Vec<String>,
    /// URL过滤 (正则表达式)
    pub url_filters: Vec<String>,
}

impl AlertRule {
    pub fn new(name: &str, severity: Severity, enabled: bool) -> Self {
        Self {
            name: name.to_string(),
            severity,
            enabled,
            rate_limit_seconds: None,
            tag_filters: Vec::new(),
            url_filters: Vec::new(),
        }
    }

    /// 检查是否应该发送告警
    pub fn should_alert(&self, result: &ScanResult) -> bool {
        if !self.enabled {
            return false;
        }

        if result.severity != self.severity {
            return false;
        }

        // 检查标签过滤
        if !self.tag_filters.is_empty() {
            let has_matching_tag = self.tag_filters.iter()
                .any(|filter| result.tags.iter().any(|tag| tag.contains(filter)));
            if !has_matching_tag {
                return false;
            }
        }

        // 检查URL过滤
        if !self.url_filters.is_empty() {
            let url_matches = self.url_filters.iter()
                .any(|filter| regex::Regex::new(filter).map(|re| re.is_match(&result.url)).unwrap_or(false));
            if !url_matches {
                return false;
            }
        }

        true
    }
}

/// 告警全局设置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertGlobalSettings {
    /// 最大告警频率 (告警/小时)
    pub max_alerts_per_hour: Option<u32>,
    /// 静默时间段
    pub quiet_hours: Option<(u8, u8)>, // (start_hour, end_hour)
    /// 重复告警间隔 (分钟)
    pub deduplication_interval_minutes: u64,
    /// 告警保留时间 (天)
    pub retention_days: u32,
}

impl Default for AlertGlobalSettings {
    fn default() -> Self {
        Self {
            max_alerts_per_hour: Some(100),
            quiet_hours: Some((22, 8)), // 22:00 - 08:00
            deduplication_interval_minutes: 30,
            retention_days: 7,
        }
    }
}

/// 告警管理器
pub struct AlertManager {
    /// 配置
    config: AlertConfig,
    /// 告警渠道
    channels: Vec<Box<dyn AlertChannel>>,
    /// 告警历史
    alert_history: std::sync::RwLock<HashMap<String, DateTime<Utc>>>,
    /// 告警统计
    stats: std::sync::RwLock<AlertStats>,
}

impl AlertManager {
    /// 创建新的告警管理器
    pub fn new(config: AlertConfig) -> Self {
        Self {
            config,
            channels: Vec::new(),
            alert_history: std::sync::RwLock::new(HashMap::new()),
            stats: std::sync::RwLock::new(AlertStats::default()),
        }
    }

    /// 添加告警渠道
    pub fn add_channel(mut self, channel: Box<dyn AlertChannel>) -> Self {
        self.channels.push(channel);
        self
    }

    /// 处理扫描结果并发送告警
    pub async fn process_scan_result(&self, result: &ScanResult) -> Result<(), ScanError> {
        if !self.config.enabled {
            return Ok(());
        }

        // 查找匹配的告警规则
        let alert_rule = self.config.rules.iter()
            .find(|rule| rule.should_alert(result));

        if alert_rule.is_none() {
            return Ok(());
        }

        let alert_rule = alert_rule.unwrap();

        // 检查全局限制
        if !self.check_global_limits(result).await? {
            return Ok(());
        }

        // 检查去重
        let dedup_key = self.generate_dedup_key(result);
        if self.is_duplicate(&dedup_key, alert_rule).await? {
            return Ok(());
        }

        // 创建告警
        let alert = Alert::from_scan_result(result);

        // 发送告警
        let mut sent_channels = 0;
        let mut failed_channels = 0;

        for channel in &self.channels {
            if !channel.is_available().await {
                warn!("Alert channel {} is not available", channel.name());
                continue;
            }

            match channel.send_alert(&alert).await {
                Ok(_) => {
                    sent_channels += 1;
                    info!("Alert sent via channel: {}", channel.name());
                }
                Err(e) => {
                    failed_channels += 1;
                    error!("Failed to send alert via channel {}: {}", channel.name(), e);
                }
            }
        }

        // 更新统计
        {
            let mut stats = self.stats.write().unwrap();
            stats.total_alerts += 1;
            stats.sent_alerts += sent_channels;
            stats.failed_alerts += failed_channels;

            *stats.by_severity.entry(alert.severity.clone()).or_insert(0) += 1;
        }

        // 记录告警历史
        {
            let mut history = self.alert_history.write().unwrap();
            history.insert(dedup_key, Utc::now());
        }

        if sent_channels > 0 {
            info!("Alert sent for {} via {} channels", result.title, sent_channels);
        } else {
            warn!("Failed to send alert for {} (all channels failed)", result.title);
        }

        Ok(())
    }

    /// 检查全局限制
    async fn check_global_limits(&self, result: &ScanResult) -> Result<bool, ScanError> {
        let settings = &self.config.global_settings;

        // 检查静默时间
        if let Some((start, end)) = settings.quiet_hours {
            let current_hour = Utc::now().hour() as u8;

            let in_quiet_hours = if start <= end {
                current_hour >= start && current_hour < end
            } else {
                // 跨午夜的情况，如 22:00 - 08:00
                current_hour >= start || current_hour < end
            };

            if in_quiet_hours {
                // 只有Critical级别告警在静默时间发送
                if result.severity != Severity::Critical {
                    info!("Alert suppressed due to quiet hours: {}", result.title);
                    return Ok(false);
                }
            }
        }

        // 检查每小时最大告警数
        if let Some(max_per_hour) = settings.max_alerts_per_hour {
            let now = Utc::now();
            let one_hour_ago = now - chrono::Duration::hours(1);

            let recent_alerts = self.alert_history.read().unwrap()
                .values()
                .filter(|&&timestamp| timestamp > one_hour_ago)
                .count();

            if recent_alerts >= max_per_hour as usize {
                warn!("Hourly alert limit reached, suppressing alert: {}", result.title);
                return Ok(false);
            }
        }

        Ok(true)
    }

    /// 生成去重键
    fn generate_dedup_key(&self, result: &ScanResult) -> String {
        format!("{}:{}:{}", result.rule_name, result.url, result.severity.as_number())
    }

    /// 检查是否重复告警
    async fn is_duplicate(&self, dedup_key: &str, _rule: &AlertRule) -> Result<bool, ScanError> {
        let history = self.alert_history.read().unwrap();

        if let Some(&last_sent) = history.get(dedup_key) {
            let interval = self.config.global_settings.deduplication_interval_minutes;
            let dedup_duration = chrono::Duration::minutes(interval as i64);

            if Utc::now() - last_sent < dedup_duration {
                info!("Duplicate alert suppressed: {}", dedup_key);
                return Ok(true);
            }
        }

        Ok(false)
    }

    /// 获取告警统计
    pub fn get_stats(&self) -> AlertStats {
        self.stats.read().unwrap().clone()
    }

    /// 清理过期的告警历史
    pub fn cleanup_expired_history(&self) {
        let retention_duration = chrono::Duration::days(self.config.global_settings.retention_days as i64);
        let cutoff_time = Utc::now() - retention_duration;

        let mut history = self.alert_history.write().unwrap();
        let initial_count = history.len();

        history.retain(|_, &mut timestamp| timestamp > cutoff_time);

        let cleaned_count = initial_count - history.len();
        if cleaned_count > 0 {
            info!("Cleaned up {} expired alert history entries", cleaned_count);
        }
    }
}

/// 告警统计
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AlertStats {
    /// 总告警数
    pub total_alerts: u64,
    /// 成功发送数
    pub sent_alerts: u64,
    /// 失败发送数
    pub failed_alerts: u64,
    /// 按严重程度统计
    pub by_severity: HashMap<Severity, u64>,
}

/// 控制台告警渠道
pub struct ConsoleAlertChannel {
    name: String,
}

impl ConsoleAlertChannel {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
        }
    }
}

#[async_trait]
impl AlertChannel for ConsoleAlertChannel {
    async fn send_alert(&self, alert: &Alert) -> Result<(), ScanError> {
        println!("\n{}", alert.formatted_message());
        Ok(())
    }

    fn name(&self) -> &str {
        &self.name
    }

    async fn is_available(&self) -> bool {
        true // 控制台总是可用的
    }
}

/// 文件告警渠道
pub struct FileAlertChannel {
    name: String,
    file_path: std::path::PathBuf,
}

impl FileAlertChannel {
    pub fn new(name: &str, file_path: impl Into<std::path::PathBuf>) -> Self {
        Self {
            name: name.to_string(),
            file_path: file_path.into(),
        }
    }
}

#[async_trait]
impl AlertChannel for FileAlertChannel {
    async fn send_alert(&self, alert: &Alert) -> Result<(), ScanError> {
        use tokio::fs::OpenOptions;
        use tokio::io::AsyncWriteExt;

        let mut file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(&self.file_path)
            .await?;

        let content = format!("{}\n{}\n{}\n", "=".repeat(80), alert.formatted_message(), "=".repeat(80));
        file.write_all(content.as_bytes()).await?;
        file.flush().await?;

        Ok(())
    }

    fn name(&self) -> &str {
        &self.name
    }

    async fn is_available(&self) -> bool {
        // 检查文件是否可写
        match tokio::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open(&self.file_path)
            .await
        {
            Ok(_) => true,
            Err(_) => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::matcher::{RuleType, Severity};

    #[tokio::test]
    async fn test_alert_creation() {
        let scan_result = ScanResult::new(
            "test_rule".to_string(),
            RuleType::Custom,
            Severity::High,
            "Test vulnerability".to_string(),
            "https://example.com/test".to_string(),
        );

        let alert = Alert::from_scan_result(&scan_result);

        assert_eq!(alert.severity, Severity::High);
        assert_eq!(alert.target_url, "https://example.com/test");
        assert_eq!(alert.rule_name, "test_rule");
        assert!(alert.id.len() > 0);
    }

    #[tokio::test]
    async fn test_console_alert_channel() {
        let channel = ConsoleAlertChannel::new("console");
        let scan_result = ScanResult::new(
            "test_rule".to_string(),
            RuleType::Custom,
            Severity::Medium,
            "Test vulnerability".to_string(),
            "https://example.com/test".to_string(),
        );

        let alert = Alert::from_scan_result(&scan_result);

        // 发送告警不会失败
        let result = channel.send_alert(&alert).await;
        assert!(result.is_ok());
        assert!(channel.is_available().await);
    }
}